package com.allwees.bs.module.fms.service.impl;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.neovisionaries.i18n.CurrencyCode;
import com.allwees.bs.core.modelbase.branch.BranchUser;
import com.allwees.bs.core.modelbase.branch.EBranchType;
import com.allwees.bs.module.base.service.impl.BoServiceImpl;
import com.allwees.bs.module.core.req.BaseAuditReq;
import com.allwees.bs.module.fms.constant.EAccountLogBsType;
import com.allwees.bs.module.fms.constant.EBalanceType;
import com.allwees.bs.module.fms.dao.MerchantAccountDao;
import com.allwees.bs.module.fms.entity.BaseAccount.WithdrawResult;
import com.allwees.bs.module.fms.entity.MerchantAccount;
import com.allwees.bs.module.fms.entity.MerchantAccountLog;
import com.allwees.bs.module.fms.entity.MerchantWithdrawAccount;
import com.allwees.bs.module.fms.req.ApplyWithdrawReq;
import com.allwees.bs.module.fms.req.AuditWithdrawReq;
import com.allwees.bs.module.fms.req.DoBatchWithdrawReq;
import com.allwees.bs.module.fms.req.DoWithDrawFailedReq;
import com.allwees.bs.module.fms.req.DoWithdrawReq;
import com.allwees.bs.module.fms.req.ResetWithdrawPasswordReq;
import com.allwees.bs.module.fms.req.SetWithdrawSecurityInfoReq;
import com.allwees.bs.module.fms.req.query.MerchantAccountQueryReq;
import com.allwees.bs.module.fms.service.MerchantAccountLogService;
import com.allwees.bs.module.fms.service.MerchantAccountService;
import com.allwees.bs.module.fms.service.MerchantWithdrawAccountService;
import com.allwees.bs.module.fms.util.FmsNoUtil;
import com.allwees.bs.module.fms.util.WithdrawPasswordVerifier;
import com.allwees.bs.module.mms.bo.MerchantCreatedBo;
import com.allwees.core.common.constant.EStatus;
import com.allwees.core.common.util.MoneyUtil;
import com.allwees.sender.sms.SmsSender;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import java.math.BigDecimal;
import java.util.Date;
import java.util.List;

/**
 * @author yangylsky
 * @since 2019-12-12
 */
@Service
public class MerchantAccountServiceImpl extends BoServiceImpl<MerchantAccountDao, MerchantAccount> implements MerchantAccountService {
    @Autowired
    private MerchantAccountLogService merchantAccountLogService;

    @Autowired
    private MerchantWithdrawAccountService merchantWithdrawAccountService;

    @Value("${system.params.order.withdraw.fee:0}")
    private String withdrawFee;

    @Value("${system.params.order.withdraw.rate:10%}")
    private String commissionRate;

    @Autowired
    private SmsSender smsSender;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean create(MerchantAccount account) {
        MerchantAccount existAccount = findDefaultMerchantAccount(account.getBranchType(), account.getBranchUuid());
        Assert.isTrue(StringUtils.isBlank(account.getUuid()), "uuid必须为空");
        Assert.isNull(existAccount, "此商户账户已存在");
        account.setAccountNo(FmsNoUtil.genAccountNo(account.getBranchType(), account.getBranchUuid()));
        return save(account);
    }

    @Override
    public MerchantAccount findDefaultMerchantAccount(EBranchType branchType, String branchUuid) {
        MerchantAccountQueryReq req = new MerchantAccountQueryReq()
                .setBranchType(branchType)
                .setBranchUuid(branchUuid);
        return findUnique(req);
    }

    @Override
    public IPage<MerchantAccount> findPage(Page<MerchantAccount> page, MerchantAccountQueryReq req) {
        return super.findPage(page, req);
    }

//    @Override
//    @Transactional(rollbackFor = Exception.class)
//    public MerchantAccount createSupplierAccount(EBranchType branchType, String branchUuid) {
//        MerchantAccount account = new MerchantAccount();
//        account.setBranchType(branchType)
//                .setSettledAmount(BigDecimal.ZERO)
//                .setWithdrawedAmount(BigDecimal.ZERO)
//                .setWithdrawingAmount(BigDecimal.ZERO)
//                .setBranchUuid(branchUuid)
//                .setBalance(BigDecimal.ZERO)
//                .setCurrency(CurrencyCode.USD.name())
//                .setAccountNo(FmsNoUtil.genAccountNo(branchType, branchUuid));
//        save(account);
//        return account;
//    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public MerchantAccount createRetailerAccount(EBranchType branchType, MerchantCreatedBo bo) {
        String branchUuid = bo.getUuid();
        MerchantAccount account = new MerchantAccount();

        account.setBranchType(branchType);
        account.setAccountType(bo.getAccountType().getValue());
        account.setBankNum(1);
        account.setAccountName(bo.getAccountOwnerName());
        account.setSettledAmount(BigDecimal.ZERO);
        account.setWithdrawedAmount(BigDecimal.ZERO);
        account.setWithdrawingAmount(BigDecimal.ZERO);
        account.setBranchUuid(branchUuid);
        account.setBalance(BigDecimal.ZERO);
        account.setInShippingAmt(BigDecimal.ZERO);
        account.setUnsettleShippingAmt(BigDecimal.ZERO);
        account.setWaitingShipAmt(BigDecimal.ZERO);
        account.setRefundAmt(BigDecimal.ZERO);
        account.setCurrency(CurrencyCode.USD.name());
        account.setMobile(bo.getMobile());
        account.setAccountNo(FmsNoUtil.genAccountNo(branchType, branchUuid));
        save(account);
        return account;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean setWithdrawSecurityInfo(SetWithdrawSecurityInfoReq req) {
        MerchantAccount account = checkByUuid(req.getAccountUuid());
//		Assert.isTrue(StringUtils.isBlank(account.getMobile()), "手机号已经设置");
//		Assert.isTrue(StringUtils.isBlank(account.getWithdrawPassword()), "提现密码已经设置");
        account.setMobile(req.getMobile());
        account.setWithdrawPassword(req.getNewPassword());
        return updateByUuid(account);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean resetWithdrawPassword(ResetWithdrawPasswordReq req) {
        MerchantAccount account = checkByUuid(req.getAccountUuid());
        Assert.isTrue(account.isSecurityInfoSet(), "请先绑定手机号和提现密码");
        Assert.isTrue(account.getMobile().equals(req.getMobile()), "手机号与当前账户绑定手机号不匹配");
        account.setWithdrawPassword(req.getNewPassword());
        return updateByUuid(account);
    }




    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean applyWithdraw(ApplyWithdrawReq req, BranchUser branchUser) {
        MerchantAccount account = checkByUuid(req.getAccountUuid());
        List<MerchantWithdrawAccount> merchantWithdrawAccountList = merchantWithdrawAccountService.findAllWithdrawAccount(branchUser.getBranchType(),branchUser.getBranchUuid());
        Assert.isTrue(account.checkMerchantAccount(merchantWithdrawAccountList,req.getBankAccountNo()),"提现账户不匹配");
        Assert.isTrue(account.isSecurityInfoSet(), "请先绑定手机号和提现密码");
        Assert.isTrue(account.getMobile().equals(req.getMobile()), "手机号与当前账户绑定手机号不匹配");
        WithdrawPasswordVerifier.verifyPassword(account, req.getPassword());

        String commissionRate = this.commissionRate;
        String fee = this.withdrawFee;
        WithdrawResult result = account.applyWithdraw2(req.getAmount(), req.getCurrency(),commissionRate,fee);
        //Assert.isTrue(MoneyUtil.equals(req.getActualAmount(),result.getWithdrawAmount()),"提现金额有误");
        MerchantWithdrawAccount withdrawAccount = merchantWithdrawAccountService.checkByUuid(req.getWithdrawAccountUuid());
        logWithdraw(account, withdrawAccount, result);
        boolean updateResult = updateByUuid(account);

        if (updateResult) {
            smsSender.applyWithdrawalSendSms(account.getMobile(), result.getAmount().toString());
        }
        return updateResult;
    }

    private void logWithdraw(MerchantAccount account, MerchantWithdrawAccount withdrawAccount, WithdrawResult result) {
        MerchantAccountLog accountLog = new MerchantAccountLog();
        accountLog.setCommission(result.getCommission())
                 .setActualAmtUsd(result.getWithdrawAmount())
                 .setBankName(withdrawAccount.getBankName())
                 .setAccountType(withdrawAccount.getType().getValue())
                 .setBranchUuid(account.getBranchUuid())
                .setBalanceType(EBalanceType.OUTGO)
                .setBsType(EAccountLogBsType.WITHDRAW)
                .setNo(FmsNoUtil.genAccountLogNo(account.getBranchUuid(), EBalanceType.OUTGO, EAccountLogBsType.WITHDRAW))
                .setAccountUuid(account.getUuid())
                .setAccountNo(account.getAccountNo())
                .setCounterpartyAccountUuid(withdrawAccount.getUuid())
                .setCounterpartyAccountNo(withdrawAccount.getAccountNo())
                .setBankAccountNo(withdrawAccount.getBankAccountNo())
                .setTakeAmount(result.getAmount())
                .setCurrency(result.getCurrency())
                .setFee(result.getFee())
                .setBalance(result.getBalance())
                .setSettledAmount(result.getSettledAmount())
                .setBalanceCurrency(result.getBalanceCurrency())
                .setEStatus(EStatus.WAIT_AUDIT);
                 //.setActualAmt()  实际到账(人民币)


        merchantAccountLogService.create(accountLog);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int auditWithdraw(AuditWithdrawReq req) {
        List<MerchantAccountLog> accountLogs = merchantAccountLogService.findByUuids(req.getAccountLogUuids());
        int succcessNum = 0;
        for (MerchantAccountLog accountLog : accountLogs) {
            if (auditWithdraw(accountLog, req)) {
                succcessNum++;
            }
        }
        return succcessNum;
    }

    protected boolean auditWithdraw(MerchantAccountLog accountLog, BaseAuditReq req) {
        Assert.notNull(accountLog, "流水不能为空");
        Assert.isTrue(EAccountLogBsType.WITHDRAW == accountLog.getBsType(), "提现记录类型错误");
        Assert.isTrue(EStatus.WAIT_AUDIT.equals(accountLog.getEStatus()), "流水[" + accountLog.getNo() + "]状态[" + accountLog.getEStatus().desc() + "]不能执行审核");
        accountLog.setEStatus(req.getResult().status());
        accountLog.setAuditTime(new Date());
        switch (req.getResult()) {
            case APPROVED:
                //
                break;
            case REJECTED:
                MerchantAccount account = findByUuid(accountLog.getAccountUuid());
                account.rejectWithdraw(accountLog.getTakeAmount(), accountLog.getFee(), accountLog.getCurrency());
                updateByUuid(account);
                break;
            default:
                throw new IllegalArgumentException("非法参数[req.getResult()]: " + req.getResult());
        }
        return merchantAccountLogService.updateByUuid(accountLog);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean doWithdraw(DoWithdrawReq req) {

        MerchantAccountLog accountLog = merchantAccountLogService.findByUuid(req.getWithdrawLogUuid());
        Assert.notNull(accountLog, "流水不能为空");
        Assert.isTrue(EAccountLogBsType.WITHDRAW == accountLog.getBsType(), "提现记录类型错误");
        Assert.isTrue(EStatus.AUDIT_APPROVED == accountLog.getEStatus(), "提现记录状态错误");
        MerchantAccount account = findByUuid(accountLog.getAccountUuid());
        account.doWithdraw(accountLog.getTakeAmount(), accountLog.getCurrency());
        updateByUuid(account);
        accountLog.setEStatus(EStatus.COMPLETED);
        accountLog.setExchangeRate(req.getExchangeRate());//设置汇率
        accountLog.setActualAmt(MoneyUtil.mul(accountLog.getActualAmtUsd(),req.getExchangeRate()));//根据汇率换算成人民币
        boolean updateResult = merchantAccountLogService.updateByUuid(accountLog);

        if (updateResult) {
            smsSender.withdrawalSuccessSendSms(account.getMobile(), accountLog.getActualAmt().toString());
        }

        return updateResult;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean doBatchWithdraw(DoBatchWithdrawReq req) {
        for (String  withDrawReqUuid : req.getAccountLogUuids()){
            MerchantAccountLog accountLog = merchantAccountLogService.findByUuid(withDrawReqUuid);
            Assert.notNull(accountLog, "["+accountLog.getNo()+"] 流水号不存在");
            Assert.isTrue(EAccountLogBsType.WITHDRAW == accountLog.getBsType(), "["+accountLog.getNo()+"] 提现记录类型错误");
            Assert.isTrue(EStatus.AUDIT_APPROVED == accountLog.getEStatus(), "["+accountLog.getNo()+"] 提现记录状态错误");
            MerchantAccount account = findByUuid(accountLog.getAccountUuid());
            Assert.notNull(account, "["+account.getAccountNo() +"] 账户不存在");
        }
        for (String  withDrawReqUuid : req.getAccountLogUuids()){
            MerchantAccountLog accountLog = merchantAccountLogService.findByUuid(withDrawReqUuid);
            Assert.notNull(accountLog, "流水不能为空");
            Assert.isTrue(EAccountLogBsType.WITHDRAW == accountLog.getBsType(), "提现记录类型错误");
            Assert.isTrue(EStatus.AUDIT_APPROVED == accountLog.getEStatus(), "提现记录状态错误");
            MerchantAccount account = findByUuid(accountLog.getAccountUuid());
            account.doWithdraw(accountLog.getTakeAmount(), accountLog.getCurrency());
            updateByUuid(account);
            accountLog.setEStatus(EStatus.COMPLETED);
            accountLog.setExchangeRate(req.getExchangeRate());//设置汇率
            accountLog.setActualAmt(MoneyUtil.mul(accountLog.getActualAmtUsd(),req.getExchangeRate()));//根据汇率换算成人民币
            boolean updateResult = merchantAccountLogService.updateByUuid(accountLog);

            if (updateResult) {
                smsSender.withdrawalSuccessSendSms(account.getMobile(), accountLog.getActualAmt().toString());
            }
        }

        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean  doWithdrawFailed(DoWithDrawFailedReq req){
        MerchantAccountLog accountLog = merchantAccountLogService.findByUuid(req.getWithdrawLogUuid());
        Assert.notNull(accountLog, "流水不能为空");
        Assert.isTrue(EAccountLogBsType.WITHDRAW == accountLog.getBsType(), "提现记录类型错误");
        Assert.isTrue(EStatus.AUDIT_APPROVED == accountLog.getEStatus(), "提现记录状态错误");
        MerchantAccount account = findByUuid(accountLog.getAccountUuid());
        account.doWithdrawFailed(accountLog.getTakeAmount(), accountLog.getCurrency());
        updateByUuid(account);
        accountLog.setEStatus(EStatus.WITH_DRAW_FAILED);
        accountLog.setComment(req.getComment());
        boolean updateResult = merchantAccountLogService.updateByUuid(accountLog);
        if (updateResult) {
            smsSender.withdrawalSuccessSendSms(account.getMobile(), accountLog.getTakeAmount().toString());
        }

        return updateResult;
    }
}
